/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/windows/nhQueue.h,v 1.5 2003/11/21 16:12:58 itsme Exp $
 *
 *
 *  this is a templated and thread safe fifo
 *
 */

#ifndef __NHQUEUE_H__

#include <vector>
#include "debug.h"

template<class M> class NHQueue {
public:
    NHQueue(int size);
    ~NHQueue();

    void Reset();

    bool Add(M msg);

    bool GetWait(M& msg);
    bool GetNoWait(M& msg);

    int Size() const;

    bool IsFull() const;
    bool IsEmpty() const;

    void DumpState();

private:
    std::vector<M> m_list;
    int m_maxSize;
    int m_head;
    int m_tail;
    int m_size;
    int m_overflows;
    int m_errors;
    int m_total_in;
    int m_total_out;
    int m_total_empty;

    HANDLE m_hEvent;
    CRITICAL_SECTION m_lock;

    bool Get(M& msg, bool bWait);
};

template<class M> NHQueue<M>::NHQueue(int size)
{
    m_list.resize(size);
    m_maxSize= size;

    InitializeCriticalSection(&m_lock);
    m_hEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
    if (m_hEvent==INVALID_HANDLE_VALUE)
        error("ERROR creating Event\n");

    Reset();
}
template<class M> void NHQueue<M>::Reset()
{
    m_size=0;
    m_head=0;
    m_tail=0;
    m_overflows= 0;
    m_errors= 0;

    m_total_in= 0;
    m_total_out= 0;
    m_total_empty= 0;
}

template<class M> NHQueue<M>::~NHQueue()
{
    DeleteCriticalSection(&m_lock);
    if (!CloseHandle(m_hEvent))
        error("ERROR closing Event handle\n");
}

// this is called from within callbacks: don't use anything
// else but criticalsection and event calls.
template<class M> bool NHQueue<M>::Add(M msg)
{
    bool bRes;

    EnterCriticalSection(&m_lock);

    m_total_in++;

    if (!IsFull())
    {
        m_list[m_tail]= msg;
        if (++m_tail == m_maxSize)
            m_tail= 0;
        ++m_size;

        if (!SetEvent(m_hEvent))
            m_errors++;

        bRes= true;
    }
    else
    {
        m_overflows++;
        bRes= false;
    }
    LeaveCriticalSection(&m_lock);

    return bRes;
}

template<class M> bool NHQueue<M>::Get(M& msg, bool bWait)
{
    if (!IsEmpty() || (bWait && WaitForSingleObject(m_hEvent, INFINITE)==WAIT_OBJECT_0))
    {
        if (!ResetEvent(m_hEvent))
            m_errors++;

        EnterCriticalSection(&m_lock);

        msg= m_list[m_head];
        if (++m_head==m_maxSize)
            m_head= 0;

        --m_size;

        LeaveCriticalSection(&m_lock);

        m_total_out++;

        return true;
    }
    if (bWait)
        m_errors++;

    m_total_empty++;

    return false;
}
template<class M> bool NHQueue<M>::GetWait(M& msg)
{
    return Get(msg, true);
}
template<class M> bool NHQueue<M>::GetNoWait(M& msg)
{
    return Get(msg, false);
}

template<class M> int NHQueue<M>::Size() const
{
    return m_size;
}
template<class M> bool NHQueue<M>::IsFull() const
{
    return (m_size==m_maxSize);
}
template<class M> bool NHQueue<M>::IsEmpty() const
{
    return (m_size==0);
}
template<class M> void NHQueue<M>::DumpState()
{
    debug("queue::state size=%d  max=%d head=%d tail=%d overflow=%d errors=%d in=%d out=%d empty=%d\n", 
        m_size, m_maxSize, m_head, m_tail, m_overflows, m_errors, m_total_in, m_total_out, m_total_empty);
}


#define __NHQUEUE_H__
#endif

